let activeEffect = undefined;

const bucket = new WeakMap();
// 追踪
const track = (target, key) => {
  if (!activeEffect) return;

  let depsMap = bucket.get(target); // WeakMap 由 target --> Map 构成；
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()));
  }
  let deps = depsMap.get(key); // Map 由 key --> Set 构成
  if (!deps) {
    depsMap.set(key, (deps = new Set()));
  }
  // 最后将当前激活的副作用函数添加到桶中
  deps.add(activeEffect);

  // deps就是一个与当前副作用函数存在关联的依赖集合
  activeEffect.deps.push(deps);
};
// 触发
const trigger = (target, key) => {
  let depsMap = bucket.get(target);
  if (!depsMap) return;
  let deps = depsMap.get(key);
  // 执行副作用函数
  const effectSet = new Set(deps);

  const effectSetRun = new Set();
  effectSet &&
    effectSet.forEach((fn) => {
      // 如果trigger触发的副作用函数与当前正在执行的副作用函数是同一个 则不触发执行
      if (fn !== activeEffect) {
        effectSetRun.add(fn);
      }
    });

  effectSetRun &&
    effectSetRun.forEach((effectFn) => {
      // 如果存在调度器，则直接调用调度器函数 并把当前副作用函数传递过去，由用户自己控制副作用函数的执行(把控制权交给用户)
      if (effectFn.options.scheduler) {
        effectFn.options.scheduler(effectFn);
      } else {
        effectFn();
      }
    });
};

// 增加lazy选项
// 定义一个栈来存储副作用函数
const effectStack = [];
const effect = (fn, options = {}) => {
  const _effectFn = () => {
    cleanup(_effectFn);
    activeEffect = _effectFn;
    // 将当前副作用函数压入栈中
    effectStack.push(_effectFn);
    const res = fn();
    // 副作用函数执行完之后 将其从栈中弹出
    effectStack.pop();
    // activeEffect始终指向栈顶的副作用函数
    activeEffect = effectStack[effectStack.length - 1];
    return res;
  };
  _effectFn.options = options;
  //  用来存储 所有与当前副作用函数相关联的依赖集合
  _effectFn.deps = [];

  // 只有非lazy的时候才执行
  if (!options.lazy) {
    _effectFn();
  }
  return _effectFn;
};

const cleanup = (effectFn) => {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i];
    deps.delete(effectFn);
  }
  effectFn.deps.length = 0;
};

// 4.9 watch的实现原理
// 实现原理，封装通用的watch函数
// 获取新值和旧值

// watch函数的实现中硬编码了对source.foo的读取
function watch(source, cb) {
  let getter;
  if (typeof source === "function") {
    getter = source;
  } else {
    getter = () => traverse(source);
  }

  let oldValue, newValue;
  const effectFn = effect(() => getter(), {
    lazy: true,
    scheduler: () => {
      newValue = effectFn();
      cb(newValue, oldValue, onInvalidate);
      oldValue = newValue;
    },
  });
  oldValue = effectFn();
}

function traverse(value, seen = new Set()) {
  if (typeof value !== "object" || value === null || seen.has(value)) return;
  seen.add(value);
  for (let key in value) {
    traverse(value[key], seen);
  }
  return value;
}

// 测试代码
const data = {
  foo: 1,
};
const obj = new Proxy(data, {
  get(target, key) {
    track(target, key);
    return target[key];
  },
  set(target, key, val) {
    target[key] = val;
    trigger(target, key);
    return true;
  },
});

// watch(obj, () => {
//   console.log("数据变了==");
// });
// getter函数
watch(
  () => obj.foo,
  async (newVal, oldVal) => {
    console.log("数据变了==", newVal, oldVal);
  }
);
console.log("obj.foo=11=", obj.foo);
obj.foo += 1;
// console.log("obj.foo=22=", obj.foo);
obj.foo += 1;
